﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DirectShowLib;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes;

namespace Servidor
{
    class Captura : ISampleGrabberCB
    {
        private ManualResetEvent imagemPronta = null;
        private IGraphBuilder graphBuilder = null;
        private ICaptureGraphBuilder2 captureGraphBuilder = null;
        private IMediaControl mediaControl = null;
        private IBaseFilter compressionFilter = null;
        private bool executando = false;

        public Captura(int framerate, int videowidth, int videoheight)
        {
            DsDevice[] dispositivosCaptura;
            dispositivosCaptura = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

            if (dispositivosCaptura.Length <= 0)
            {
                throw new Exception("Nenhum dispositivo de captura encontrado");
            }

            try
            {
                foreach (DsDevice dispositivo in dispositivosCaptura)
                {
                    EncerrarInterfaces();
                    InicializarInterfaces();
                    DefinirGraficoCaptura(dispositivo, framerate, videowidth, videoheight);
                    imagemPronta = new ManualResetEvent(false);
                    executando = false;
                }
            }
            catch (Exception ex)
            {
                Dispose();
                throw;
            }
        }

        private void DefinirGraficoCaptura(DsDevice device, int framerate, int videowidth, int videoheight)
        {
            int hr;
            IBaseFilter dev = (IBaseFilter)device;
            hr = graphBuilder.AddFilter(dev, device.Name);
            DsError.ThrowExceptionForHR(hr);

            IBaseFilter asfWriter = null;
            IFileSinkFilter fileSinkFilter = null;
            hr = captureGraphBuilder.SetOutputFileName(MediaSubType.Asf, "video.wmv", out asfWriter, out fileSinkFilter);
            DsError.ThrowExceptionForHR(hr);
            try
            {
                IConfigAsfWriter configAsfWriter = (IConfigAsfWriter)asfWriter;
                //Guid category = new Guid(
            }
            finally
            {
                if (fileSinkFilter != null)
                {
                    Marshal.ReleaseComObject(fileSinkFilter);
                    fileSinkFilter = null;
                }
            }

            hr = graphBuilder.AddFilter(compressionFilter, "Filtro de compressao");
            DsError.ThrowExceptionForHR(hr);
            //hr = captureGraphBuilder.RenderStream(PinCategory.Capture, MediaType.Video, device, compressionFilter, baseGrabFilter);
            DsError.ThrowExceptionForHR(hr);
        }

        private void SalvarInformacaoTamanho(ISampleGrabber sampleGrabber)
        {
            int hr;
            AMMediaType midia = new AMMediaType();
            hr = sampleGrabber.GetConnectedMediaType(midia);
            DsError.ThrowExceptionForHR(hr);
            if (midia.formatType != FormatType.VideoInfo || midia.formatPtr == IntPtr.Zero)
            {

            }
        }

        private void InicializarInterfaces()
        {
            graphBuilder = (IGraphBuilder)new FilterGraph();
            captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            mediaControl = (IMediaControl)this.graphBuilder;
            int hr = captureGraphBuilder.SetFiltergraph(graphBuilder);
            DsError.ThrowExceptionForHR(hr);

            InicializarFiltroCompressao();
        }

        private void InicializarFiltroCompressao()
        {
            object recurso = null;
            Guid iid = typeof(IBaseFilter).GUID;
            foreach (DsDevice d in DsDevice.GetDevicesOfCat(FilterCategory.VideoCompressorCategory))
            {
                if (d.Name.CompareTo("WMVideo9 Encoder DMO") == 0)
                {
                    d.Mon.BindToObject(null, null, ref iid, out recurso);
                    break;
                }
            }

            compressionFilter = (IBaseFilter)recurso;
        }

        private void Dispose()
        {
            EncerrarInterfaces();
            if (imagemPronta != null)
            {
                imagemPronta.Close();
                imagemPronta = null;
            }
        }

        ~Captura()
        {
            Dispose();
        }

        private void EncerrarInterfaces()
        {
            int hr;
            try
            {
                if (mediaControl != null)
                {
                    hr = mediaControl.Stop();
                    executando = false;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

            if (graphBuilder != null)
            {
                Marshal.ReleaseComObject(graphBuilder);
                graphBuilder = null;
            }

            if (compressionFilter != null)
            {
                Marshal.ReleaseComObject(compressionFilter);
                compressionFilter = null;
            }
        }

        private void DefinirParametros(ICaptureGraphBuilder2 captureGraph, IBaseFilter filtroCaptura, int frameRate, int width, int height)
        {
            int hr;
            object obj;
            AMMediaType midia;

            hr = captureGraph.FindInterface(PinCategory.Capture, MediaType.Video, filtroCaptura, typeof(IAMStreamConfig).GUID, out obj);

            IAMStreamConfig videoStreamConfig = (IAMStreamConfig)obj;
            if (videoStreamConfig == null)
            {
                throw new Exception("Falha ao obter IAMStreamConfig");
            }

            hr = videoStreamConfig.GetFormat(out midia);
            DsError.ThrowExceptionForHR(hr);

            VideoInfoHeader v = new VideoInfoHeader();
            Marshal.PtrToStructure(midia.formatPtr, v);

            if (frameRate > 0)
            {
                v.AvgTimePerFrame = 10000000 / frameRate;
            }

            if (width > 0)
            {
                v.BmiHeader.Width = width;
            }

            if (height > 0)
            {
                v.BmiHeader.Height = height;
            }

            Marshal.StructureToPtr(v, midia.formatPtr, false);

            hr = videoStreamConfig.SetFormat(midia);
            DsError.ThrowExceptionForHR(hr);

            DsUtils.FreeAMMediaType(midia);
            midia = null;
        }

        private void ConfigurarSampleGrabber(ISampleGrabber sampleGrabber)
        {
            AMMediaType midia;
            int hr;

            midia = new AMMediaType();
            midia.majorType = MediaType.Video;
            midia.subType = MediaSubType.Asf;
            midia.formatType = FormatType.VideoInfo;
            hr = sampleGrabber.SetMediaType(midia);
            DsError.ThrowExceptionForHR(hr);

            DsUtils.FreeAMMediaType(midia);
            midia = null;

            hr = sampleGrabber.SetCallback(this, 1);
            DsError.ThrowExceptionForHR(hr);
        }

        #region ISampleGrabberCB Members

        public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
        {
            throw new NotImplementedException();
        }

        public int SampleCB(double SampleTime, IMediaSample pSample)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
